package query;

import heap.HeapFile;
import heap.HeapScan;
import index.HashIndex;
import global.Minibase;
import global.RID;
import global.SearchKey;
import parser.AST_Update;
import relop.Predicate;
import relop.Schema;
import relop.Tuple;

/**
 * Execution plan for updating tuples.
 */
class Update implements Plan {

	/**
	 * Optimizes the plan, given the parsed query.
	 * 
	 * @throws QueryException if invalid column names, values, or pedicates
	 */
	protected String table_name;
	protected Predicate [][] predicates;
	protected Schema schema;
	protected Object []objs;
	protected String []update_these_cols;


	public Update(AST_Update tree) throws QueryException {
		objs = tree.getValues();
		table_name = tree.getFileName();
		predicates = tree.getPredicates();
		schema = Minibase.SystemCatalog.getSchema(table_name);
		update_these_cols = tree.getColumns();

		QueryCheck.tableExists(table_name);

	} // public Update(AST_Update tree) throws QueryException

	/**
	 * Executes the plan and prints applicable output.
	 */
	public void execute() {
		HeapFile hf = new HeapFile (table_name);
		HeapScan hs = hf.openScan();

		RID rid = new RID();
		byte []b;
		Tuple t;
		int row_updated_count = 0;
		IndexDesc []idesc = Minibase.SystemCatalog.getIndexes(table_name);
		boolean table_has_index = (idesc.length == 0)? false : true;

		while (hs.hasNext()){
			b = hs.getNext(rid);
			t = new Tuple (schema, b);
			boolean eval = false;                   // done on based of OR operator, so its OK if any of predicate is true

			for (Predicate []arr : predicates) {
				// for inner loop, every predicate shud be satisfied.
				
				for (Predicate p : arr){
					eval = eval | p.evaluate(t);
					if (eval == true)
						break;
				}
				if (eval == false)
					break;
			}

			if (eval) {
				row_updated_count++;

				// create a new tuple with updated values
				Tuple updated_tuple = new Tuple (schema, b);	// old tuple
				int c=0;
				for (String update_column : update_these_cols){
					updated_tuple.setField(update_column, objs[c++]);
				}

				if (table_has_index){
					for (IndexDesc id : idesc){
						// check Shruti
						HashIndex hi = new HashIndex(id.indexName);

						// if id.columnName is one in the update_these_cols, then in trouble
						for (String update_column : update_these_cols){
							if (update_column.equalsIgnoreCase(id.columnName)){
								// delete old entry from hash, then insert new entry
								Object o = t.getField(id.columnName);
								SearchKey sk = new SearchKey (o);
								hi.deleteEntry(sk, rid);				// hashIndex
								o = updated_tuple.getField(id.columnName);
								sk = new SearchKey(o);
								hi.insertEntry(sk, rid);
							}
						}
					}
				}
				
				hf.updateRecord(rid, updated_tuple.getData());

			}
		}

		// print the output message
		System.out.println(row_updated_count + " rows affected. ");
		hs.close();

	} // public void execute()

} // class Update implements Plan
